/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
#include "PassiveAI.h"
#include "SpellScript.h"

enum Spells
{
	SPELL_POISON_CLOUD						= 28240,
	SPELL_MUTATING_INJECTION				= 28169,
	SPELL_SLIME_SPRAY_10					= 28157,
	SPELL_SLIME_SPRAY_25					= 54364,
	SPELL_POISON_CLOUD_DAMAGE_AURA_10		= 28158,
	SPELL_POISON_CLOUD_DAMAGE_AURA_25		= 54362,
	SPELL_BERSERK							= 26662,

	SPELL_BOMBARD_SLIME						= 28280, // Spawn slime when hit by slime spray
};

enum Events
{
	EVENT_SPELL_BERSERK						= 1,
	EVENT_SPELL_POISON_CLOUD				= 2,
	EVENT_SPELL_SLIME_SPRAY					= 3,
	EVENT_SPELL_MUTATING_INJECTION			= 4,
};

enum Misc
{
	NPC_FALLOUT_SLIME						= 16290,
	NPC_SEWAGE_SLIME						= 16375,
};

class boss_grobbulus : public CreatureScript
{
public:
    boss_grobbulus() : CreatureScript("boss_grobbulus") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_grobbulusAI (pCreature);
    }

	struct boss_grobbulusAI : public ScriptedAI
	{
		boss_grobbulusAI(Creature *c) : ScriptedAI(c), summons(me)
		{
			pInstance = me->GetInstanceScript();
		}

		EventMap events;
		SummonList summons;
		InstanceScript* pInstance;
		uint32 dropSludgeTimer;

		void Reset()
		{
			events.Reset();
			summons.DespawnAll();
			dropSludgeTimer = 0;

			if (pInstance)
				pInstance->SetData(EVENT_GROBBULUS, NOT_STARTED);
		}

		void EnterCombat(Unit *who)
		{
			me->SetInCombatWithZone();
			events.ScheduleEvent(EVENT_SPELL_POISON_CLOUD, 15000);
			events.ScheduleEvent(EVENT_SPELL_MUTATING_INJECTION, 20000);
			events.ScheduleEvent(EVENT_SPELL_SLIME_SPRAY, 10000);
			events.ScheduleEvent(EVENT_SPELL_BERSERK, RAID_MODE(12*MINUTE*IN_MILLISECONDS, 9*MINUTE*IN_MILLISECONDS));

			if (pInstance)
				pInstance->SetData(EVENT_GROBBULUS, IN_PROGRESS);
		}

		void SpellHitTarget(Unit *target, const SpellInfo* spellInfo)
		{
			if (spellInfo->Id == RAID_MODE(SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25) && target->GetTypeId() == TYPEID_PLAYER)
				me->SummonCreature(NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
		}

		void JustSummoned(Creature* cr)
		{
			if (cr->GetEntry() == NPC_FALLOUT_SLIME)
				cr->SetInCombatWithZone();

			summons.Summon(cr);
		}

		void SummonedCreatureDespawn(Creature* summon){ summons.Despawn(summon); }

		void JustDied(Unit*)
		{
			summons.DespawnAll();
			if (pInstance)
				pInstance->SetData(EVENT_GROBBULUS, DONE);
		}

		void KilledUnit(Unit* who)
		{
			if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
				pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
		}

		void UpdateAI(uint32 diff)
		{
			// Some nice visuals
			dropSludgeTimer += diff;
			if (!me->IsInCombat() && dropSludgeTimer >= 5000)
			{
				if (me->IsWithinDist3d(3178, -3305, 319, 5.0f) && !summons.HasEntry(NPC_SEWAGE_SLIME))
					me->CastSpell(3128.96f+irand(-20, 20), -3312.96f+irand(-20, 20), 293.25f, SPELL_BOMBARD_SLIME, false);

				dropSludgeTimer = 0;
			}

			if (!UpdateVictim())
				return;

			events.Update(diff);
			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_SPELL_POISON_CLOUD:
					me->CastSpell(me, SPELL_POISON_CLOUD, true);
					events.RepeatEvent(15000);
					break;
				case EVENT_SPELL_BERSERK:
					me->CastSpell(me, SPELL_BERSERK, true);
					events.PopEvent();
					break;
				case EVENT_SPELL_SLIME_SPRAY:
					me->MonsterTextEmote("Grobbulus sprays slime across the room!", 0, true);
					me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25), false);
					events.RepeatEvent(20000);
					break;
				case EVENT_SPELL_MUTATING_INJECTION:
					if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true, -SPELL_MUTATING_INJECTION))
						me->CastSpell(target, SPELL_MUTATING_INJECTION, false);

					events.RepeatEvent(8000 + uint32(120 * me->GetHealthPct()));
					break;
			}

			DoMeleeAttackIfReady();
		}
	};
};

class boss_grobbulus_poison_cloud : public CreatureScript
{
public:
    boss_grobbulus_poison_cloud() : CreatureScript("boss_grobbulus_poison_cloud") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_grobbulus_poison_cloudAI(pCreature);
    }

	struct boss_grobbulus_poison_cloudAI : public NullCreatureAI
	{
		boss_grobbulus_poison_cloudAI(Creature* pCreature) : NullCreatureAI(pCreature)
		{
		}

		uint32 sizeTimer;
		uint32 auraVisualTimer;

		void Reset()
		{
			sizeTimer = 0;
			auraVisualTimer = 1;
			me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f);
			me->setFaction(21); // Grobbulus one	
		}

		void KilledUnit(Unit* who)
		{
			if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
				me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
		}

		void UpdateAI(uint32 diff)
		{
			// this has to be delayed to be visible :/
			if (auraVisualTimer)
			{
				auraVisualTimer += diff;
				if (auraVisualTimer >= 1000)
				{
					me->CastSpell(me, (me->GetMap()->Is25ManRaid() ? SPELL_POISON_CLOUD_DAMAGE_AURA_25 : SPELL_POISON_CLOUD_DAMAGE_AURA_10), true);
					auraVisualTimer = 0;
				}
			}

			sizeTimer += diff;
			// increase size to 15yd in 60 seconds, 0.00025 is the growth of size in 1ms
			me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f+(0.00025f*sizeTimer));
		}
	};

};

class spell_grobbulus_poison : public SpellScriptLoader
{
    public:
        spell_grobbulus_poison() : SpellScriptLoader("spell_grobbulus_poison") { }

        class spell_grobbulus_poison_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_grobbulus_poison_SpellScript);

            void FilterTargets(std::list<WorldObject*>& targets)
            {
                std::list<WorldObject*> tmplist;
				for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
					if (GetCaster()->IsWithinDist3d((*itr), 0.0f))
						tmplist.push_back(*itr);

				 targets.clear();
				 for( std::list<WorldObject*>::iterator itr = tmplist.begin(); itr != tmplist.end(); ++itr )
					 targets.push_back(*itr);
            }

            void Register()
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_grobbulus_poison_SpellScript();
        }
};

void AddSC_boss_grobbulus()
{
    new boss_grobbulus();
    new boss_grobbulus_poison_cloud();
	new spell_grobbulus_poison();
}